
package com.gobrs.async.core.timer;

import com.gobrs.async.core.common.util.ThreadPoolUtil;
import com.gobrs.async.core.config.ConfigManager;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;


/**
 * The type Gobrs timer.
 */
public class GobrsTimer {

    private static final Logger logger = LoggerFactory.getLogger(GobrsTimer.class);

    private static GobrsTimer INSTANCE;


    /**
     * The Timer core pool size.
     */
    private Integer timerCorePoolSize;

    private GobrsTimer(Integer timerCorePoolSize) {
        this.timerCorePoolSize = timerCorePoolSize;
    }

    /**
     * Retrieve the global instance.
     *
     * @param timerCorePoolSize the timer core pool size
     * @return the instance
     */
    public static GobrsTimer getInstance(Integer timerCorePoolSize) {

        if (INSTANCE == null) {
            synchronized (GobrsTimer.class) {
                if (INSTANCE != null) {
                    return INSTANCE;
                }

                if (Objects.isNull(timerCorePoolSize)) {
                    timerCorePoolSize = ThreadPoolUtil.calculateCoreNum();
                }
                return INSTANCE = new GobrsTimer(timerCorePoolSize);

            }
        }
        return INSTANCE;
    }

    /**
     * Reset.
     */
    public static void reset() {
        ScheduledExecutor ex = INSTANCE.executor.getAndSet(null);
        if (ex != null && ex.getThreadPool() != null) {
            ex.getThreadPool().shutdownNow();
        }
    }

    /**
     * The Executor.
     */
    AtomicReference<ScheduledExecutor> executor = new AtomicReference<ScheduledExecutor>();

    /**
     * Add timer listener.
     *
     * @param listener the listener
     * @return the timer reference
     */
    public Reference<TimerListener> addTimerListener(final TimerListener listener) {

        startThreadIfNeeded();

        TimerListenerRunnable r = new TimerListenerRunnable(listener);
//        Runnable r = () -> {
//            try {
//                listener.tick();
//            } catch (Exception e) {
//                logger.error(Strings.EMPTY, e);
//            }
//        };

        ScheduledFuture<?> f = executor.get().getThreadPool().scheduleAtFixedRate(r,
                listener.getIntervalTimeInMilliseconds(), listener.getIntervalTimeInMilliseconds(), TimeUnit.MILLISECONDS);
        return new TimerReference(listener, f);

    }

    /**
     * Start thread if needed.
     */
    protected void startThreadIfNeeded() {
        // create and start thread if one doesn't exist
        while (executor.get() == null || !executor.get().isInitialized()) {
            if (executor.compareAndSet(null, new ScheduledExecutor(timerCorePoolSize))) {
                // initialize the executor that we 'won' setting
                executor.get().initialize();
            }
        }
    }

}
